﻿//========================================================= 
/**@file screenshot.h
 * @brief 截取屏幕判断颜色的Zabbix监控项
 * 
 * @date 2018-08-28   09:51:53
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_SCREENSHOT_H_
#define _LIBZHOUYB_SCREENSHOT_H_
//--------------------------------------------------------- 
#include "../../extension/ability/win_helper/WinHelper.h"
using zhou_yb::extension::ability::WinHelper;

#include "../../extension/ability/FolderHelper.h"
using zhou_yb::extension::ability::FileHelper;
using zhou_yb::extension::ability::FolderHelper;

#include "../../extension/ability/StringHelper.h"
using zhou_yb::extension::ability::StringHelper;

#include "../../extension/ability/win_helper/HWndHelper.h"
using zhou_yb::extension::ability::HWndHelper;
using zhou_yb::extension::ability::ForgroundHWnd;
using zhou_yb::extension::ability::AttachInputHWnd;
using zhou_yb::extension::ability::MoveHWnd;

#include "../rapidjson/rapidjson_extractor.h"
using zhou_yb::wrapper::rapidjson_extractor::RapidJsonArgParser;
//--------------------------------------------------------- 
namespace zhou_yb {
namespace wrapper {
namespace zabbix_extractor {
//--------------------------------------------------------- 
/// 带颜色的坐标点
struct ColorPoint
{
    LONG X;
    LONG Y;
    COLORREF Color;
};
/// 带颜色的区域
struct ColorRect
{
    LONG Left;
    LONG Top;
    DWORD Width;
    DWORD Height;
    COLORREF Color;
};
/// 屏幕截取回调
typedef void(*fpScreenShotCallback)(pointer pArg, HWND hWnd, HDC hDC, 
    const RECT& rect, const IStringArguments& customer, string& rlt);
/// 屏幕截取回调数据
struct ScreenShotParam
{
    bool IsMove;
    bool IsScreenShot;
    POINT MovePos;
    DWORD Width;
    DWORD Height;
    uint DelayTime;
    StringArguments Customer;
    string Path;
    string Result;

    pointer CallbackArg;
    fpScreenShotCallback Callback;

    ScreenShotParam() : CallbackArg(NULL), Callback(NULL) {}
};
//--------------------------------------------------------- 
/**
 * @brief 截取屏幕按钮RGB颜色状态的监控项
 * @date 2018-08-28 09:53
 */
class screenshot_monitor
{
protected:
    screenshot_monitor() {}
    /// 返回当前需要截图生成的文件名
    static string CaptureFileName(const char* dir, const char* name, size_t maxCount)
    {
        if(FolderHelper::IsFolderExist(dir))
        {
            list_t<string> bmps;
            string filter = _strput(name);
            filter += "_*.bmp";
            WinHelper::EnumFiles(dir, &bmps, NULL, filter.c_str());
            size_t count = bmps.size();
            while((count > 0) && (count >= maxCount))
            {
                FolderHelper::EraseFile(bmps.front().c_str());
                bmps.pop_front();
                --count;
            }
        }
        else
        {
            FolderHelper::CreateFolder(dir);
        }

        string capName = _strput(dir);
        capName += "\\";
        capName += _strput(name);
        if(maxCount > 1)
        {
            ByteBuilder sys_time(32);
            get_minisec_systime(sys_time);
            /* "2014-05-26 12:32:15.562" => "20140526_123215_562" */
            StringConvert::Remove(sys_time, ':');
            StringConvert::Remove(sys_time, '-');
            StringConvert::Replace(sys_time, ' ', '_');
            StringConvert::Replace(sys_time, '.', '_');

            capName += "_";
            capName += sys_time.GetString();
        }
        capName += ".bmp";
        return capName;
    }
    /// 处理每个窗口的截图参数
    static void HandleHWnd(HWND hWnd, ScreenShotParam& param)
    {
        ForgroundHWnd forgroundHwnd(hWnd, param.IsScreenShot);
        MoveHWnd moveHwnd;
        if(param.IsMove)
        {
            moveHwnd.Move(hWnd, param.MovePos.x, param.MovePos.y, param.Width, param.Height);
        }
        AttachInputHWnd inputHwnd(hWnd);
        Timer::Wait(param.DelayTime);

        bool isBmp = (param.Path.length() > 0);
        HDC hDC = NULL;
        // rect为相对于HDC的坐标位置
        RECT rect;
        GetWindowRect(hWnd, &rect);
        if(param.IsScreenShot)
        {
            if(isBmp) HWndHelper::ScreenShotToBmp(hWnd, param.Path.c_str());
            hDC = GetWindowDC(NULL);
        }
        else
        {
            if(isBmp) HWndHelper::SaveHwndToBmp(hWnd, param.Path.c_str());
            hDC = GetWindowDC(hWnd);

            LONG w = rect.right - rect.left;
            LONG h = rect.bottom - rect.top;

            rect.right = w;
            rect.bottom = h;
            rect.left = 0;
            rect.top = 0;
        }
        if(param.Callback != NULL)
        {
            param.Callback(param.CallbackArg, hWnd, hDC, rect, param.Customer, param.Result);
        }
        ReleaseDC(param.IsScreenShot ? NULL : hWnd, hDC);
    }
    /// 捕获窗口
    static BOOL Capture(const IStringArguments& arg, ScreenShotParam& param)
    {
        string sProcess = arg["Process"].To<string>();
        string caption = arg["Caption"].To<string>("");
        list_t<HWND> hWnds;
        // 没有找到进程
        if(WinHelper::EnumHwnd(sProcess.c_str(), caption.c_str(), hWnds) < 1)
            return FALSE;
        string dir;
        param.Path = "";
        if(arg.Get("CaptureDir", dir))
        {
            StringHelper::FormatDir(dir);
            size_t maxCount = arg["MaxCount"].To<size_t>(1);
            param.Path = CaptureFileName(dir.c_str(), sProcess.c_str(), maxCount);
        }
        string sLeft;
        string sTop;
        param.IsMove = arg.Get("Left", sLeft) && arg.Get("Top", sTop);
        if(param.IsMove)
        {
            param.MovePos.x = arg["Left"].To<int>(0);
            param.MovePos.y = arg["Top"].To<int>(0);
        }
        param.Width = arg["Width"].To<DWORD>(0);
        param.Height = arg["Height"].To<DWORD>(0);
        param.IsScreenShot = arg["IsScreenShot"].To<bool>(false);
        param.DelayTime = arg["DelayTime"].To<uint>(0);
        string& sCustomer = arg["Customer"].To<string>();
        RapidJsonArgParser parser;
        parser.Parse(ByteArray(sCustomer.c_str(), sCustomer.length()), param.Customer);
        HandleHWnd(hWnds.front(), param);
        return TRUE;
    }
public:
    static string put_err(int err)
    {
        return ArgConvert::ToString<int>(-err);
    }
    static COLORREF parseRGB(const string& color)
    {
        list_t<string> rgbs;
        StringHelper::Split(ByteArray(color.c_str(), color.length()), rgbs, ',');
        while(rgbs.size() < 3)
            rgbs.push_back("0");
        byte r = 0, g = 0, b = 0;
        list_t<string>::iterator itr = rgbs.begin();
        r = ArgConvert::FromString<byte>(*itr);
        ++itr;
        g = ArgConvert::FromString<byte>(*itr);
        ++itr;
        b = ArgConvert::FromString<byte>(*itr);
        return RGB(r, g, b);
    }

    static int execute(const char* cfgJson, fpScreenShotCallback callback, pointer callbackArg, string& rlt)
    {
        ScreenShotParam param;
        string sJson = FileHelper::ReadToEnd(cfgJson);
        if(sJson.length() < 1) return DeviceError::ArgIsNullErr;

        RapidJsonArgParser parser;
        size_t count = parser.Parse(ByteArray(sJson.c_str(), sJson.length()), param.Customer);
        if(count < 1) return DeviceError::ArgFormatErr;

        param.Callback = callback;
        param.CallbackArg = (callbackArg == NULL) ? (&param) : callbackArg;
        if(!Capture(param.Customer, param))
            return DeviceError::OperatorErr;
        rlt = param.Result;
        return DeviceError::Success;
    }
    static string execute(int argc, const char* argv[], fpScreenShotCallback callback)
    {
        if(argc < 1)
            return put_err(DeviceError::ArgErr);
        string rlt = "";
        int err = execute(argv[1], callback, NULL, rlt);
        if(err == DeviceError::Success)
            return rlt;
        return put_err(err);
    }
};
//--------------------------------------------------------- 
/**
 * @brief 截取屏幕区域并判断屏幕区域指定坐标RGB值是否相等
 * @date 2018-09-11 08:30
 */
class screenshot_capture_monitor
{
public:
    typedef string value_type;

    static bool parseColor(const string& sJson, list_t<ColorPoint>& colors)
    {
        StringArguments arg;
        RapidJsonArgParser parser;
        parser.Parse(ByteArray(sJson.c_str(), sJson.length()), arg);

        list_t<ColorPoint>::iterator itr = colors.push_back();
        itr->X = arg["X"].To<LONG>(0);
        itr->Y = arg["Y"].To<LONG>(0);
        string sRGB;
        bool isRGB = arg.Get("RGB", sRGB);
        itr->Color = screenshot_monitor::parseRGB(sRGB);
        return isRGB;
    }
    static bool parseColors(const IStringArguments& arg, list_t<ColorPoint>& colorPoints)
    {
        list_t<string> colors;
        list_t<string>::iterator citr;
        arg.Get("Color", colors);
        bool isRgb = false;
        for(citr = colors.begin(); citr != colors.end(); ++citr)
        {
            if(parseColor(*citr, colorPoints))
                isRgb = true;
        }
        return isRgb;
    }
    static void capturePoints(HDC hDC, const RECT& rect, list_t<ColorPoint>& colorPoints)
    {
        list_t<ColorPoint>::iterator itr;
        for(itr = colorPoints.begin(); itr != colorPoints.end(); ++itr)
        {
            int x = itr->X + rect.left;
            int y = itr->Y + rect.top;
            itr->Color = GetPixel(hDC, x, y);
        }
    }
    /// 屏幕抓图像素点提取
    static void ScreenShotCallback(pointer pArg, HWND hWnd, HDC hDC,
        const RECT& rect, const IStringArguments& customer, string& rlt)
    {
        list_t<ColorPoint> colorPoints;
        bool isCompare = parseColors(customer, colorPoints);

        list_t<ColorPoint>::const_iterator itr;
        string sRlt = "";
        bool isOK = false;
        for(itr = colorPoints.begin(); itr != colorPoints.end(); ++itr)
        {
            int x = itr->X + rect.left;
            int y = itr->Y + rect.top;
            COLORREF c = GetPixel(hDC, x, y);
            if(!isCompare)
            {
                sRlt += _hex(GetRValue(c));
                sRlt += _hex(GetGValue(c));
                sRlt += _hex(GetBValue(c));
                sRlt += ',';
            }
            else
            {
                if(c == itr->Color)
                {
                    isOK = true;
                    break;
                }
            }
        }
        if(!isCompare)
        {
            if(sRlt.length() > 0) sRlt.pop_back();
        }
        else
        {
            sRlt = (isOK ? "1" : "0");
        }
        rlt = sRlt;
    }

    static value_type execute(int argc, const char* argv[])
    {
        return screenshot_monitor::execute(argc, argv, ScreenShotCallback);
    }
};
//--------------------------------------------------------- 
/**
 * @brief 截取屏幕区域并返回是否包含指定RGB值的像素点
 * @date 2018-09-11 08:33
 */
class screenshot_compare_monitor
{
public:
    typedef string value_type;

    static bool parseRect(const string& sJson, ColorRect& rect)
    {
        StringArguments arg;
        RapidJsonArgParser parser;
        parser.Parse(ByteArray(sJson.c_str(), sJson.length()), arg);

        rect.Left = arg["Left"].To<LONG>(0);
        rect.Top = arg["Top"].To<LONG>(0);
        rect.Width = arg["Width"].To<LONG>(0);
        rect.Height = arg["Height"].To<LONG>(0);
        string& sRGB = arg["RGB"].To<string>("");
        rect.Color = screenshot_monitor::parseRGB(sRGB);
        return arg["IsCompareAll"].To<bool>(false);
    }
    /// 屏幕抓图区域颜色检测
    static void ScreenShotCallback(pointer pArg, HWND hWnd, HDC hDC,
        const RECT& rect, const IStringArguments& customer, string& rlt)
    {
        list_t<string> colors;
        customer.Get("Color", colors);

        list_t<string>::iterator itr;
        string sRlt = "";
        for(itr = colors.begin(); itr != colors.end(); ++itr)
        {
            ColorRect r;
            bool isContains = false;
            bool isCompareAll = parseRect(*itr, r);
            // 没有需要计算的点
            if(r.Width < 1 || r.Height < 1)
            {
                sRlt += "0";
                continue;
            }
            // 截取图片数据
            ByteBuilder bmpBuff;
            BITMAP bmp = { 0 };
            HWndHelper::CaptureBmpToBuffer(hDC, r.Left + rect.left, r.Top + rect.top, r.Width, r.Height, bmpBuff, &bmp);
            // 计算矩形的对角线上的点集合
            if(!isCompareAll)
            {
                // 计算对角线长度
                double line = sqrt(r.Width * r.Width + r.Height * r.Height);
                double nX = static_cast<double>(r.Width) / line;
                double nY = static_cast<double>(r.Height) / line;

                DWORD count = static_cast<DWORD>(line);
                for(DWORD i = 0; i <= count; ++i)
                {
                    DWORD x = static_cast<DWORD>(nX * i);
                    DWORD y = static_cast<DWORD>(nY * i);

                    COLORREF c = HWndHelper::GetColor(bmpBuff, x, y, bmp);
                    if(c == r.Color)
                    {
                        isContains = true;
                        break;
                    }
                }
            }
            else
            {
                for(DWORD i = 0; i < r.Width; ++i)
                {
                    for(DWORD j = 0; j < r.Height; ++j)
                    {
                        COLORREF c = HWndHelper::GetColor(bmpBuff, i, j, bmp);
                        if(c == r.Color)
                        {
                            isContains = true;
                            // 退出外层循环
                            i = r.Width;
                            break;
                        }
                    }
                }
            }
            sRlt += (isContains ? "1" : "0");
        }
        if(sRlt.length() < 1) sRlt += "0";
        rlt = sRlt;
    }

    static value_type execute(int argc, const char* argv[])
    {
        return screenshot_monitor::execute(argc, argv, ScreenShotCallback);
    }
};
//--------------------------------------------------------- 
} // namespace zabbix_extractor 
} // namespace wrapper
} // namespace zhou_yb 
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_SCREENSHOT_H_
//========================================================= 